package cn.wrl.user.controller;

import cn.hutool.core.date.DateUtil;
import cn.wrl.auth.service.AuthService;
import cn.wrl.auth.service.pojo.Account;
import cn.wrl.auth.service.pojo.AuthResponse;
import cn.wrl.controller.BaseController;
import cn.wrl.pojo.IMOOCJSONResult;
import cn.wrl.pojo.ShopcartBO;
import cn.wrl.user.pojo.Users;
import cn.wrl.user.pojo.bo.UserBO;
import cn.wrl.user.pojo.vo.UsersVO;
import cn.wrl.user.service.UsersService;
import cn.wrl.utils.CookieUtils;
import cn.wrl.utils.JsonUtils;
import cn.wrl.utils.MD5Utils;
import cn.wrl.utils.RedisOperator;
import com.google.common.net.HttpHeaders;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * 用户通行证
 */
@Slf4j
@Api(value = "注册登录", tags = {"用于注册和登录的相关接口"})
@RestController
@RequestMapping("passport")
public class PassportController extends BaseController {

    @Autowired
    private UsersService usersService;
    @Autowired
    private RedisOperator redisOperator;

    @Autowired
    private AuthService authService;

    private static final String REFRESH_TOKEN_HEADER = "refresh-token";
    private static final String USERID = "user-id";

    @ApiOperation(value = "用户名是否存在", httpMethod = "GET")
    @GetMapping("usernameIsExist")
    public IMOOCJSONResult usernameIsExist(@RequestParam String username) {
        //1.判断用户名不可为空
        if (StringUtils.isBlank(username)) {
            return IMOOCJSONResult.errorMsg("用户名不能为空");
        }
        //2.查找注册的用户名是否存在
        boolean isExist = usersService.queryUsernameIsExist(username);
        if (isExist) {
            return IMOOCJSONResult.errorMsg("用户名已存在");
        }
        return IMOOCJSONResult.ok();
    }

    @ApiOperation(value = "用户注册", httpMethod = "POST")
    @PostMapping("regist")
    public IMOOCJSONResult regist(@RequestBody UserBO userBO,
                                  HttpServletRequest request,
                                  HttpServletResponse response) {
        String username = userBO.getUsername();
        String password = userBO.getPassword();
        String confirmPassword = userBO.getConfirmPassword();
        //1.判断用户名或密码不能为空
        if (StringUtils.isBlank(username) ||
                StringUtils.isBlank(password) ||
                StringUtils.isBlank(confirmPassword)) {
            return IMOOCJSONResult.errorMsg("用户名或密码不能为空");
        }
        //2.查询用户名是否存在
        boolean isExist = usersService.queryUsernameIsExist(username);
        if (isExist) {
            return IMOOCJSONResult.errorMsg("用户名已存在");
        }
        //3.判断密码长度不少于6
        if (password.length() < 6) {
            return IMOOCJSONResult.errorMsg("密码长度不能少于6");
        }
        //4.判断两次密码是否一致
        if (!StringUtils.equals(password, confirmPassword)) {
            return IMOOCJSONResult.errorMsg("两次密码不一致");
        }
        Users userResult = usersService.createUser(userBO);

        UsersVO usersVO = setTokenUser(userResult);

        //设置cookie
        CookieUtils.setCookie(request, response, "user",
                JsonUtils.objectToJson(usersVO), true);

        //同步购物车数据
        syncShopCartData(usersVO.getId(), request, response);

        return IMOOCJSONResult.ok();
    }

    @ApiOperation(value = "用户登录", httpMethod = "POST")
    @PostMapping("login")
//    @HystrixCommand(
//            commandKey = "loginFail",   //全局唯一的标志服务，默认函数名称
//            groupKey = "password",      //全局服务分组；用于组织仪表盘、统计信息。默认类名
//            fallbackMethod = "loginFail",    //降级方法。同一个类里  public/private
////            ignoreExceptions = {IllegalArgumentException.class}       //在列表中的exception，不会触发降级
//            //线程有关的属性
//            //线程组。多个服务可以共用一个线程组
//            threadPoolKey = "threadPoolA",
//            threadPoolProperties = {
//                    //核心线程数
//                    @HystrixProperty(name = "coreSize", value = "20"),
//                    // size > 0  LinkedBlockingQueue -> 请求等待的队列
//                    // 默认 -1   SynchronousQueue -> 不存储元素的阻塞队列（建议读源码，很多CAS应用）
//                    @HystrixProperty(name = "maxQueueSize", value = "40"),
//                    //maxQueueSize=-1的时候无效，队列没有达到maxQueueSize依然拒绝
////                    @HystrixProperty(name = "queueRejectionThreshold", value = "15"),
//                    //（线程池）统计窗口持续时间
//                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1024"),
//                    //（线程池）窗口内桶子的数量
//                    @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "18"),
////            },
////            commandProperties = {
//                    // todo 熔断降级相关属性，也可以放在这里
//            }
//
//    )
    public IMOOCJSONResult login(@RequestBody UserBO userBO,
                                 HttpServletRequest request,
                                 HttpServletResponse response) {
        String username = userBO.getUsername();
        String password = userBO.getPassword();
        //1.判断用户名或密码不能为空
        if (StringUtils.isBlank(username) ||
                StringUtils.isBlank(password)) {
            return IMOOCJSONResult.errorMsg("用户名或密码不能为空");
        }
        String md5Password = MD5Utils.getMD5Str(userBO.getPassword());
        Users userResult = usersService.queryUserForLogin(userBO.getUsername(), md5Password);
        if (userResult == null) {
            return IMOOCJSONResult.errorMsg("用户名或密码错误");
        }

        //设置token
        AuthResponse tokenResponse = authService.tokenize(userResult.getId());
        if (!tokenResponse.isOk()) {
            log.error("token error. userId={}", userResult.getId());
            return IMOOCJSONResult.errorMsg("token error");
        }
        Account acc = tokenResponse.getAccount();
        response.setHeader(HttpHeaders.AUTHORIZATION, acc.getToken());
        response.setHeader(REFRESH_TOKEN_HEADER, acc.getRefreshToken());
        response.setHeader(USERID, acc.getUserId());
        response.setHeader("token-exp-time", DateUtil.offsetDay(new Date(), 1).getTime() + "");


        UsersVO usersVO = setTokenUser(userResult);

        //设置cookie
        CookieUtils.setCookie(request, response, "user",
                JsonUtils.objectToJson(usersVO), true);
        //同步购物车数据
        syncShopCartData(userResult.getId(), request, response);
        return IMOOCJSONResult.ok(userResult);
    }

    private IMOOCJSONResult loginFail(UserBO userBO,
                                      HttpServletRequest request,
                                      HttpServletResponse response,
                                      Throwable throwable) {
        log.warn("login触发熔断.");
        return IMOOCJSONResult.errorMsg("验证码输入错误（仿）");
    }

    /**
     * 注册成功以后，同步cookie和redis中的购物车数据
     */
    //todo 放到购物车模块中
    private void syncShopCartData(String userId,
                                  HttpServletRequest request,
                                  HttpServletResponse response) {
        /*
            1.redis中有数据：cookie中购物车有商品，如果与redis中的商品相同，直接删除redis中的商品，以cookie为准。（参考京东）
                           cookie中购物车为空，把redis数据同步至cookie
            2.redis中无数据：cookie中购物车有商品，直接把数据放入redis
                           cookie中购物车为空，不做任何处理
            最后要保持redis和cookie的购物车数据一致
         */
        //从redis里面拿购物车数据
        String shopCartRedis = redisOperator.get(FOODIE_SHOPCART + ":" + userId);
        //从cookie里面拿购物车数据
        String shopCartCookie = CookieUtils.getCookieValue(request, FOODIE_SHOPCART, true);
        if (StringUtils.isNotBlank(shopCartRedis)) {
            if (StringUtils.isNotBlank(shopCartCookie)) {
                //合并。有商品冲突，cookie覆盖redis
                List<ShopcartBO> shopCartListRedis = JsonUtils.jsonToList(shopCartRedis, ShopcartBO.class);
                List<ShopcartBO> shopCartListCookie = JsonUtils.jsonToList(shopCartCookie, ShopcartBO.class);
                List<String> specIdsCookie = shopCartListCookie.stream()
                        .map(ShopcartBO::getSpecId).collect(Collectors.toList());
                for (ShopcartBO scRedis : shopCartListRedis) {
                    if (!specIdsCookie.contains(scRedis.getSpecId())) {
                        shopCartListCookie.add(scRedis);
                    }
                }
                String newShopCartData = JsonUtils.objectToJson(shopCartListCookie);
                CookieUtils.setCookie(request, response, FOODIE_SHOPCART, newShopCartData, true);
                redisOperator.set(FOODIE_SHOPCART + ":" + userId, newShopCartData);
            } else {
                CookieUtils.setCookie(request, response, FOODIE_SHOPCART, shopCartRedis, true);
            }
        } else {
            if (StringUtils.isNotBlank(shopCartCookie)) {
                redisOperator.set(FOODIE_SHOPCART + ":" + userId, shopCartCookie);
            }
        }
    }

    @ApiOperation(value = "用户退出登录", httpMethod = "GET")
    @PostMapping("logout")
    public IMOOCJSONResult logout(@RequestParam String userId,
                                  HttpServletRequest request,
                                  HttpServletResponse response) {

        Account account = Account.builder()
                .token(request.getHeader(HttpHeaders.AUTHORIZATION))
                .refreshToken(request.getHeader(REFRESH_TOKEN_HEADER))
                .userId(userId).build();
        AuthResponse tokenResponse = authService.delete(account);
        if (!tokenResponse.isOk()) {
            log.error("token error. userId={}", userId);
            return IMOOCJSONResult.errorMsg("token error");
        }


        //设置cookie
        CookieUtils.deleteCookie(request, response, "user");
        // 用户退出登录，清除购物车
        CookieUtils.deleteCookie(request, response, FOODIE_SHOPCART);

        redisOperator.del(REDIS_USER_COOKIE + ":" + userId);

        return IMOOCJSONResult.ok();
    }


    public UsersVO setTokenUser(Users userResult) {
        //存入redis
        String uuid = UUID.randomUUID().toString().trim();
        redisOperator.set(REDIS_USER_COOKIE + ":" + userResult.getId(), uuid);

        UsersVO usersVO = new UsersVO();
        BeanUtils.copyProperties(userResult, usersVO);
        usersVO.setUserUniqueToken(uuid);
        return usersVO;
    }
}
